﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using Newtonsoft.Json;

namespace NGM.MicrosoftConnect {
    /*
     TODO : 
     *      Calculate when the token is expired based on Expires fro mthe time it was created... 
     *      Break out tables?
     */

    public class MicrosoftConnectOAuthRequest {
        private static string Code { get { return HttpContext.Current.Request.QueryString["code"]; } }
        
        public string ClientId { get; internal set; }
        public string ClientSecret { get; internal set; }
        public GrantType GrantType { get; internal set; }
        public Display Display { get; internal set; }
        public CultureInfo Locale { get; internal set; }
        public Uri RedirectUri { get; internal set; }
        public ResponseType ResponseType { get; internal set; }
        public List<Scope> Scopes { get; internal set; }
        public string State { get; internal set; }
        
        public string RefreshToken { get; internal set; }

        public static bool HasRequestCode() {
            if (string.IsNullOrEmpty(Code))
                return false;

            return true;
        }

        private Uri GenerateRequestUri() {
            UriBuilder builder = new UriBuilder(MicrosoftConnectConstants.LiveOAuthPointer + "token");
            AppendParameter(builder, "client_id", this.ClientId);
            AppendParameter(builder, "client_secret", this.ClientSecret);
            AppendParameter(builder, "display", this.Display.ToString().ToLowerInvariant());

            if (Locale != null)
                AppendParameter(builder, "locale", this.Locale.Name);

            AppendParameter(builder, "redirect_uri", this.RedirectUri.ToString());
            AppendParameter(builder, "response_type", this.ResponseType.ToString().ToLowerInvariant());
            AppendParameter(builder, "grant_type", this.GrantType.ToString().ToLowerInvariant());

            if (this.GrantType == GrantType.Refresh_Token) {
                AppendParameter(builder, "refresh_token", this.RefreshToken);
            } else {
                AppendParameter(builder, "scope", this.GenerateFormattedScopeString());
                AppendParameter(builder, "state", this.State);
                AppendParameter(builder, "code", Code);
            }

            return builder.Uri;
        }

        private void AppendParameter(UriBuilder uriBuilder, string paramName, string paramValue) {
            if (string.IsNullOrEmpty(paramValue))
                return;

            if (uriBuilder.Query != null && uriBuilder.Query.Length > 1)
                uriBuilder.Query = uriBuilder.Query.Substring(1) + "&" + paramName + "=" + HttpUtility.UrlEncode(paramValue);
            else
                uriBuilder.Query = paramName + "=" + HttpUtility.UrlEncode(paramValue);
        }

        private string GenerateFormattedScopeString() {
            var stringBuilder = new StringBuilder();
            foreach (var extendedPermission in this.Scopes) {
                stringBuilder.AppendFormat("{0},", extendedPermission);
            }
            stringBuilder.Remove(stringBuilder.Length - 1, 1);
            return stringBuilder.ToString();
        }

        public MicrosoftConnectOAuthResponse GetResponse() {
            var requestUri = GenerateRequestUri();

            try {
                MicrosoftConnectWebRequestBuilder builder = new MicrosoftConnectWebRequestBuilder(requestUri.GetLeftPart(UriPartial.Path));
                builder.WithContentType(MicrosoftConnectConstants.ApplicationFormUrlEncodedContentType);
                builder.WithWebRequestType(System.Net.WebRequestMethods.Http.Post);
                var webRequest = builder.Build();

                using (StreamWriter writer = new StreamWriter(webRequest.GetRequestStream())) {
                    writer.Write(requestUri.Query.TrimStart('?'));
                }

                using (var webResponse = webRequest.GetResponse())
                using (var responseStream = webResponse.GetResponseStream()) {
                    if (responseStream == null)
                        return null;

                    using (var streamReader = new StreamReader(responseStream, Encoding.UTF8)) {
                        return JsonConvert.DeserializeObject<MicrosoftConnectOAuthResponse>(streamReader.ReadLine());
                    }
                }
            } catch (WebException webException) {
                string responseBody = null;
                if (webException.Response != null) {
                    using (var sr = new StreamReader(webException.Response.GetResponseStream(), Encoding.UTF8)) {
                        responseBody = sr.ReadToEnd();
                    }
                }
                throw new Exception(String.Format(
                    "Failure occurred contacting consent service: Response=\r\n\r\n----\r\n{0}\r\n----\r\n\r\n", responseBody), webException);
            } catch (Exception innerException) {
                throw new Exception("Failed to get access token. Ensure that the verifier token provided is valid.", innerException);
            }
        }
    }
}